#include <WiFi.h>
#include <WiFiMulti.h>
#include <WebServer.h>
#include <PubSubClient.h>

void wificonnect();
void connectMQTTserver();
void receiveCallback(char *topic, byte *payload, unsigned int length);
void subscribeTopic();

WiFiMulti wifimulti;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

const int subQoS = 1;            // 客户端订阅主题时使用的QoS级别（截止2020-10-07，仅支持QoS = 1，不支持QoS = 2）
const bool cleanSession = false; // 清除会话（如QoS>0必须要设为false）

const char *willTopic = "willTopic";    // 遗嘱主题名称
const char *willMsg = "willMsg";        // 遗嘱主题信息
const int willQos = 0;                  // 遗嘱QoS
const int willRetain = false;           // 遗嘱保留
const char *mqttUserName = "test-user"; // 服务端连接用户名
const char *mqttPassword = "ranye-iot"; // 服务端连接密码
const char *mqttServer = "test.ranye-iot.net";

void setup()
{
    Serial.begin(115200);
    wificonnect();

    mqttClient.setServer(mqttServer, 1883);
    mqttClient.setCallback(receiveCallback);
    connectMQTTserver();
}

void loop()
{
    if (!mqttClient.connected())
    {
        connectMQTTserver();
    }

    // 处理信息以及心跳
    mqttClient.loop();
}

void connectMQTTserver()
{
    // 根据ESP8266的MAC地址生成客户端ID（避免与其它ESP8266的客户端ID重名）
    String clientId = "client-" + WiFi.macAddress();

    /* 连接MQTT服务器
  boolean connect(const char* id, const char* user, 
                  const char* pass, const char* willTopic, 
                  uint8_t willQos, boolean willRetain, 
                  const char* willMessage, boolean cleanSession); 
  若让设备在离线时仍然能够让qos1工作，则connect时的cleanSession需要设置为false                
                  */
    if (mqttClient.connect(clientId.c_str(), mqttUserName,
                           mqttPassword, willTopic,
                           willQos, willRetain, willMsg, cleanSession))
    {
        Serial.print("MQTT Server Connected. ClientId: ");
        Serial.println(clientId);
        Serial.print("MQTT Server: ");
        Serial.println(mqttServer);
        subscribeTopic(); // 订阅指定主题
    }
    else
    {
        Serial.print("MQTT Server Connect Failed. Client State:");
        Serial.println(mqttClient.state());
        delay(5000);
    }
}

void receiveCallback(char *topic, byte *payload, unsigned int length)
{
    Serial.print("Message Received [");
    Serial.print(topic);
    Serial.print("] ");
    for (int i = 0; i < length; i++)
    {
        Serial.print((char)payload[i]);
    }
    Serial.println("");
    Serial.print("Message Length(Bytes) ");
    Serial.println(length);

    if ((char)payload[0] == '1')
    { // 如果收到的信息以“1”为开始
        //digitalWrite(BUILTIN_LED, LOW);  // 则点亮LED。
        Serial.println("RECEIVER 1");
    }
    else
    {
        //digitalWrite(BUILTIN_LED, HIGH); // 否则熄灭LED。
        Serial.println("RECEIVER ANOTHER");
    }
}

void subscribeTopic()
{
    // 建立订阅主题。主题名称以Taichi-Maker-Sub为前缀，后面添加设备的MAC地址。
    // 这么做是为确保不同设备使用同一个MQTT服务器测试消息订阅时，所订阅的主题名称不同
    String topicString = "LINKTEST" + WiFi.macAddress();
    char subTopic[topicString.length() + 1];
    strcpy(subTopic, topicString.c_str());

    // 通过串口监视器输出是否成功订阅主题以及订阅的主题名称
    // 请注意subscribe函数第二个参数数字为QoS级别。这里为QoS = 1
    if (mqttClient.subscribe(subTopic, subQoS))
    {
        Serial.print("Subscribed Topic: ");
        Serial.println(subTopic);
    }
    else
    {
        Serial.print("Subscribe Fail...");
    }
}

void wificonnect()
{
    //在这里添加你需要的WiFi账号密码
    wifimulti.addAP("LAGRANGE", "abcdef..");
    wifimulti.addAP("OpenWRT-2.4G", "skabcdef");
    wifimulti.addAP("MEIZU 16s Pro", "skabcdef");
    wifimulti.addAP("Feng", "1234567890");
    int i = 0;
    Serial.println("connecting.....");
    while (wifimulti.run() != WL_CONNECTED)
    {
        delay(1000);
        Serial.print(i);
        Serial.print(' ');
        i++;
        if (i > 20)
            break;
    }
    if (WiFi.status() != WL_CONNECTED)
        Serial.println("connect timeout");
    else
    {
        Serial.println("WiFi Connect");
        Serial.print("IP address:");
        Serial.println(WiFi.localIP());
    }
}